const axios = require('axios');
const config = require('../config/config');

/**
 * LLM服务类
 */
class LLMService {
  /**
   * 修复常见的JSON格式错误
   * @param {string} jsonStr - 可能有错误的JSON字符串
   * @returns {string} - 修复后的JSON字符串
   */
  fixCommonJsonErrors(jsonStr) {
    let fixed = jsonStr;
    
    // 1. 修复缺少开头引号的字段名（如 birth_date" → "birth_date"）
    // 匹配模式：换行或逗号后，空格，然后是字母开头但缺少引号的字段名
    fixed = fixed.replace(/([,\n]\s*)([a-zA-Z_][a-zA-Z0-9_]*":\s*)/g, '$1"$2');
    
    // 2. 修复多余的逗号（如 {..., } 或 [..., ]）
    fixed = fixed.replace(/,(\s*[}\]])/g, '$1');
    
    // 3. 修复缺少逗号的情况（如 "field1": value "field2"）
    fixed = fixed.replace(/([}\]"0-9])\s*\n\s*"/g, '$1,\n  "');
    
    // 4. 修复单引号（JSON标准使用双引号）
    // 注意：只替换键名和字符串值的单引号，不影响字符串内容中的单引号
    fixed = fixed.replace(/'([^']*?)':/g, '"$1":');
    
    return fixed;
  }

  /**
   * 调用LLM API进行数据抽取
   * @param {string} biography - 简介文本（建议使用中文简介以提高准确性）
   * @param {Object} context - 可选的上下文信息（如当前职务、军衔等）
   * @returns {Promise<Object>} - 抽取的结构化数据
   */
  async extractPersonnelInfo(biography, context = {}) {
    // 记录简介长度（用于排查问题）
    const originalLength = biography ? biography.length : 0;
    console.log(`📝 简介长度: ${originalLength} 字符`);

    // 不截断简介，使用完整内容
    // 注意：对于超长简介，LLM处理时间会更长
    const processedBiography = biography || '';

    // 检测简介语言（是否包含中文字符）
    const isChinese = /[\u4e00-\u9fa5]/.test(processedBiography);
    const languageHint = isChinese ? '中文' : '英文';
    console.log(`📝 简介语言: ${languageHint}`);

    // 构建上下文信息提示（中文）
    let contextHintCn = '';
    let contextHintEn = '';
    if (context.position || context.rank) {
      const contextPartsCn = [];
      const contextPartsEn = [];
      // console.log(`context.position: ${context.position}, context.rank: ${context.rank}`);
      
      // 如果有职务，先翻译为中文
      if (context.position) {
        // 检查是否包含中文
        const hasChinese = /[\u4e00-\u9fa5]/.test(context.position);
        const positionCn = hasChinese ? context.position : await this.translateToChinese(context.position);
        
        contextPartsCn.push(`当前职务: ${positionCn}`);
        contextPartsEn.push(`Current Position: ${context.position}`);
      }
      
      // 如果有军衔，先翻译为中文
      if (context.rank) {
        // 检查是否包含中文
        const hasChinese = /[\u4e00-\u9fa5]/.test(context.rank);
        const rankCn = hasChinese ? context.rank : await this.translateToChinese(context.rank);
        
        contextPartsCn.push(`军衔: ${rankCn}`);
        contextPartsEn.push(`Military Rank: ${context.rank}`);
      }
      
      contextHintCn = `\n**已知信息（可作为参考）：**\n${contextPartsCn.join('\n')}\n`;
      contextHintEn = `\n**Known Information (for reference):**\n${contextPartsEn.join('\n')}\n`;
      console.log(`📌 上下文信息: ${contextPartsCn.join(', ')}`);
    }

    const maxRetries = 2; // 最大重试次数
    let lastError;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        if (attempt > 1) {
          console.log(`第 ${attempt} 次尝试调用LLM...`);
        }

        // 根据语言使用不同的提示词
        const prompt = isChinese 
          ? `从以下军事人员简介中提取结构化信息，仅返回有效的JSON格式数据（不要任何解释）。
${contextHintCn ? contextHintCn : contextHintEn}
简介：
${processedBiography}

**JSON格式要求（非常重要）：**
- 必须返回严格符合JSON标准的格式
- 所有字段名必须用双引号包围（如 "birth_date"，不要写成 birth_date"）
- 字符串值必须用双引号
- 数字值不要用引号
- 不要在最后一个字段后加逗号
- 确保所有括号正确配对

返回JSON格式：
{
  "country_region": "国家/地区（国家/地区的中文名称，如美国、日本、台湾、韩国等）",
  "chinese_name": "中文姓名",
  "english_name": "英文姓名",
  "gender": "male/female/unknown",
  "birth_date": "日期格式（只有年份用YYYY，有年月用YYYY-MM，完整日期用YYYY-MM-DD）",
  "age": 年龄数字（当前年份为2025年11月，如1979年出生，则年龄为2025-1979=46岁）,
  "email": "电子邮件",
  "phone": "电话",
  "graduate_school": "最高学历对应的毕业院校（中文名称）",
  "education_level": "最高学历/学位（中文名称）",
  "military_branch": "军兵种（陆军/海军/空军/太空军/海军陆战队）",
  "military_rank": "军衔",
  "organization": "当前所在机构",
  "current_position": "当前职务",
  "position_start_date": "日期格式（当前职位的开始日期，从工作经历中最新的那条记录提取开始时间，只有年份用YYYY，有年月用YYYY-MM，完整日期用YYYY-MM-DD）",
  "work_status": "active/retired/reserve（在役/退役/预备役）",
  "work_experience": [{"time":"日期范围（保持原文精度，如原文是1994-06至1997-06就写1994-06至1997-06，原文是1994至1997就写1994至1997；如果原文没有明确时间信息，使用空字符串\"\"）","organization":"机构","position":"职务"}],
  "education_experience": [{"time":"日期范围（保持原文精度，如原文是1979至1983就写1979至1983，原文是1979-06至1983-05就写1979-06至1983-05；如果原文没有明确时间信息，使用空字符串\"\"）","school":"学校","major":"专业/学位/课程/培训"}]
}

**重要说明：**
- **education_experience（教育经历）**：应包含**所有**教育记录，包括学位、培训、课程、证书、研讨会等，不要遗漏任何教育经历
- **education_level 和 graduate_school**：只选择**最高正式学位**（按下面的规则选择）
- **birth_date 和 age**：只能从原文中**直接提取**，如果原文没有明确提到出生日期或年龄，必须使用null，**绝对不要根据教育经历、工作经历等信息推测**
- **position_start_date（任职日期）**：必须从**work_experience**中提取，查找最新的工作记录（通常是"至今"或时间最晚的那条），提取其**开始时间**，保持原文精度

# 重要提示：

## 日期格式（非常重要）：
**严格保持原文的日期精度，绝对不要自动补充月份或日期！**

- 日期字段保持原始精度，不要自动补全
- 只有年份的用"YYYY"格式（如：1979）
- 有年月的用"YYYY-MM"格式（如：1979-06）
- 完整日期用"YYYY-MM-DD"格式（如：1979-06-15）
- **如果原文没有明确时间信息，使用空字符串""，绝对不要推测或捏造日期**

**工作经历和教育经历的time字段（特别重要）：**
- ❌ 错误示例：原文"1985–1990年"，提取成"1985-01至1990-01"（自动补充了月份）
- ✅ 正确示例：原文"1985–1990年"，应提取为"1985至1990"（保持原文精度）
- ❌ 错误示例：原文"1994年6月至1997年6月"，提取成"1994-06-01至1997-06-01"（自动补充了日）
- ❌ 错误示例：原文"毕业于哈佛大学"（无时间信息），提取成"1990至1994"（凭空捏造）
- ✅ 正确示例：原文"毕业于哈佛大学"（无时间信息），应提取为空字符串""（不推测）

## 工作经历提取规则（特别重要）：
**必须区分"当前职位"和"历任职位"，不要把所有职位都标记为"至今"！**

**严禁基于时间线推测（这是最常见的错误）：**
- ❌ 不要因为"1994年入伍"就推测早期工作都是"1994年"开始
- ❌ 不要因为"2003年成为军官"就推测之后的职位从"2003年"开始
- ❌ 不要因为简历中提到某个时间点就推测附近的职位发生在那个时间
- ✅ 每条工作经历必须有**直接的、明确的时间描述**才能填写时间
- ✅ 如果原文只说"担任过"、"曾在...工作"，没有说时间，time就是空字符串""

1. **当前职位的识别标志**（只有这些情况才使用"至今"）：
   - 明确说"至今"、"现任"、"接任"、"担任"（现在时）
   - 例如："2024年7月接任第十三辖区的海军上士职务" → time: "2024-07至今"

2. **历任职位的识别标志**（这些情况不要使用"至今"）：
   - 明确说"此前"、"曾担任"、"历任"
   - 例如："此前曾担任联合跨机构特遣部队南部的海军上士" → time: ""（无明确时间）
   - 例如："并历任多个职位，包括..." → time: ""（无明确时间）

3. **时间信息处理**：
   - 有明确时间范围：如"1994至1997年担任..." → time: "1994至1997"
   - 有开始时间，无结束时间，且是当前职位：如"2024年7月接任..." → time: "2024-07至今"
   - **只有开始时间，无结束时间，且非当前职位**：如"2007年被任命为..." → time: "2007"（不要推测结束时间）
   - 无明确时间信息：time留空 ""
   - **禁止推测或捏造日期**
   - **如果文中没有指定结束时间，不要自动推测或补充结束时间**

**错误示例❌：**
简介："弗劳顿上士于2024年7月接任第十三辖区的海军上士职务。此前曾担任联合跨机构特遣部队南部的海军上士，并历任多个职位，包括卡普梅训练中心、查尔斯顿海域部门..."

错误提取：
- 第十三辖区 → time: "2024-07至今" ✓（正确）
- 联合跨机构特遣部队南部 → time: "2024-07至今" ❌（错误！"此前"表示过去，且无明确时间）
- 卡普梅训练中心 → time: "2024-07至今" ❌（错误！"历任"表示过去，且无明确时间）

**正确示例✅：**
正确提取：
- {"time":"2024-07至今","organization":"第十三辖区","position":"海军上士"}
- {"time":"","organization":"联合跨机构特遣部队南部","position":"海军上士"}
- {"time":"","organization":"卡普梅训练中心","position":"指挥官"}
- {"time":"","organization":"查尔斯顿海域部门","position":"指挥官"}

**示例2（只有开始时间，没有结束时间）：**
简介："2007年，他被任命为工程值班军官。他的岸上任职经历包括：在中大西洋区域维护中心（MARMC）工作；在作战测试与评估部队（OPTEVFOR）担任AEGIS系统的作战测试主管..."

错误提取❌：
- {"time":"2007至今","organization":"...","position":"工程值班军官"} ❌（非当前职位，不应该用"至今"）
- {"time":"2007至2010","organization":"中大西洋区域维护中心","position":"..."} ❌（推测了结束时间）

正确提取✅：
- {"time":"2007","organization":"...","position":"工程值班军官"}（只有开始时间）
- {"time":"","organization":"中大西洋区域维护中心","position":"..."}（无明确时间）
- {"time":"","organization":"作战测试与评估部队","position":"AEGIS系统的作战测试主管"}（无明确时间）

**示例3（基于时间线推测的错误）：**
简介："罗谢尔上尉的海军生涯始于1994年9月，当时他是一名普通海军士兵。...此后，他在'KINKAID'号驱逐舰（DD 965）上担任'战斧'导弹的消防控制员。2003年5月，罗谢尔上尉从奥本大学获得电气工程学士学位，并通过士兵晋升计划正式成为海军军官。他曾先后在'BOONE'号护卫舰（FFG 28）担任电气军官，在'VICKSBURG'号巡洋舰（CG 69）担任消防控制军官。"

错误提取❌（基于时间线推测）：
- {"time":"1994-09至2003-05","organization":"KINKAID号驱逐舰","position":"战斧导弹消防控制员"} ❌（原文只说"他在...上担任"，没有说时间！不要因为"1994年入伍"和"2003年毕业"就推测）
- {"time":"2003-05","organization":"BOONE号护卫舰","position":"电气军官"} ❌（原文只说"曾担任"，没有说时间！）
- {"time":"2003-05","organization":"VICKSBURG号巡洋舰","position":"消防控制军官"} ❌（原文只说"担任"，没有说时间！）

正确提取✅：
- {"time":"","organization":"KINKAID号驱逐舰","position":"战斧导弹消防控制员"}（原文只说"担任"，无时间）
- {"time":"","organization":"BOONE号护卫舰","position":"电气军官"}（原文只说"曾担任"，无时间）
- {"time":"","organization":"VICKSBURG号巡洋舰","position":"消防控制军官"}（原文只说"担任"，无时间）

**关键点**：虽然原文提到"1994年9月入伍"和"2003年5月成为军官"，但这**不能**作为推测其他工作经历时间的依据！

**示例4（多个日期混淆-非常重要）：**
简介："特雷诺尔少将现任国防后勤局武器支援部门指挥官。该部门成立于2025年10月1日。在2025年7月28日接任现职之前，特雷诺尔少将曾担任美国舰队司令部舰队军械与补给主管。"

**关键信息**：
- "2025年7月28日接任现职" ← 个人任职时间
- "部门成立于2025年10月1日" ← 机构成立时间（与个人任职无关）

错误提取❌（混淆了个人任职时间和机构成立时间）：
- {"time":"2025-10至今","organization":"国防后勤局武器支援部门","position":"指挥官"} ❌（错误！使用了部门成立时间，而不是任职时间）

正确提取✅（使用个人任职时间）：
- {"time":"2025-07至今","organization":"国防后勤局武器支援部门","position":"指挥官"}（使用"2025年7月28日接任"）
- {"time":"","organization":"美国舰队司令部","position":"舰队军械与补给主管"}（只说"曾担任"，无明确时间）

**时间提取优先级规则（非常重要）**：
当原文中存在多个日期时，必须严格区分：
1. ✅ **个人任职相关的时间**（最高优先级）：
   - "2025年7月28日接任" → 使用 2025-07
   - "自2020年起担任" → 使用 2020
   - "2024年1月到任" → 使用 2024-01
   
2. ❌ **机构/组织相关的时间**（绝对不要使用）：
   - "部门成立于2025年10月1日" ← 这是机构成立时间，不是个人任职时间
   - "2026年投入运营" ← 这是机构运营时间，不是个人任职时间
   - "单位于2020年改组" ← 这是机构变更时间，不是个人任职时间

**判断方法**：
- 看时间的主语是谁：
  - "张三于2025年7月接任..." ✅（主语是张三，是个人任职时间）
  - "该部门成立于2025年10月..." ❌（主语是部门，是机构时间）
  - "单位于2020年改编..." ❌（主语是单位，是机构时间）

## 教育经历提取规则（特别重要）：
**禁止推测或捏造教育时间，如果原文没有明确时间信息，time字段使用空字符串""**

**严禁基于时间线推测（这是最常见的错误）：**
- ❌ 不要因为"1994年入伍"就推测所有早期培训都是"1994年"开始
- ❌ 不要因为"2003年毕业"就推测之前的培训结束于"2003年"或"1997年"
- ❌ 不要因为简历中提到某个时间点就推测附近的事件发生在那个时间
- ✅ 每条教育经历必须有**直接的、明确的时间描述**才能填写时间
- ✅ 如果原文只说"完成了新兵训练"、"进入A学校学习"，没有说时间，time就是空字符串""

**错误示例❌（凭空捏造日期）：**
简介："毕业于查尔斯顿大学。他所获得的学术学位包括金融理学学士学位和战略领导力硕士学位。此外，他还毕业于美国陆军军士长学院..."

错误提取：
- 查尔斯顿大学，金融理学学士学位 → time: "1994至1997" ❌（原文无时间，凭空捏造）
- 查尔斯顿大学，战略领导力硕士学位 → time: "1994至1997" ❌（原文无时间，凭空捏造）

**错误示例❌（基于时间线推测）：**
简介："罗谢尔上尉的海军生涯始于1994年9月，当时他是一名普通海军士兵。他在伊利诺伊州的大湖地区完成了新兵训练，并随后进入消防控制员'A'学校学习，之后转学到加利福尼亚州圣地亚哥的'战斧'武器系统'C'学校深造。2003年5月，罗谢尔上尉从奥本大学获得电气工程学士学位。罗谢尔上尉还获得了海军研究生院的应用物理学硕士学位。"

错误提取（基于时间线推测）：
- {"time":"1994-09至1997-06","school":"大湖地区新兵训练营","major":"新兵训练"} ❌（原文只说"完成了新兵训练"，没有说时间！不要因为前面提到"1994年9月入伍"就推测是1994年）
- {"time":"1994-09至1997-06","school":"消防控制员'A'学校","major":"..."} ❌（原文只说"进入A学校学习"，没有说时间！）
- {"time":"1994-09至1997-06","school":"战斧武器系统C学校","major":"..."} ❌（原文只说"转学到C学校"，没有说时间！）
- {"time":"2025","school":"海军研究生院","major":"应用物理学硕士学位"} ❌（原文只说"获得了硕士学位"，没有说时间！）

**关键点**：虽然原文提到"1994年9月入伍"和"2003年5月毕业"，但这**不能**作为推测其他教育经历时间的依据！

**正确示例✅（同样的简介，正确提取）：**
正确提取：
- {"time":"","school":"大湖地区新兵训练营","major":"新兵训练"}（原文只说"完成了"，无时间）
- {"time":"","school":"消防控制员'A'学校","major":"消防控制员培训"}（原文只说"进入学习"，无时间）
- {"time":"","school":"战斧武器系统'C'学校","major":"战斧武器系统培训"}（原文只说"转学到"，无时间）
- {"time":"2003-05","school":"奥本大学","major":"电气工程学士学位"}（原文明确说"2003年5月获得"）
- {"time":"","school":"海军研究生院","major":"应用物理学硕士学位"}（原文只说"获得了"，无时间）

**对比说明**：
- "1994年9月入伍"是入伍时间，不是新兵训练的时间
- 新兵训练、A学校、C学校都没有直接的时间描述，所以time都是空字符串
- 只有奥本大学有明确的"2003年5月"，才填写时间

**有完整时间信息的正确示例：**
简介："1979至1983年就读于美国海军学院，获得理学学士学位"
正确提取：
- {"time":"1979至1983","school":"美国海军学院","major":"理学学士学位"}
- ✅ 正确示例：原文"1994年6月至1997年6月"，应提取为"1994-06至1997-06"（保持原文精度）
- 日期范围格式：开始时间至结束时间（如：1994-06至1997-06、1985至1990、2024-04至今）
- 如果仍在职或仍在就读，结束时间用"至今"（如：2024-04至今、2020至今）
- **原文没有月份，就不要添加月份；原文没有日期，就不要添加日期**

**特别重要：函授课程和培训课程也要提取时间（如果原文有明确标注）**
错误示例❌（遗漏时间）：
简介："2000年：通过函授方式完成麦克斯韦尔空军基地的中队军官学校课程。2004年：通过函授方式完成空军指挥与参谋学院的学习。2012年：通过函授方式完成空军战争学院的学习"

错误提取（忽略了时间）：
- {"time":"","school":"麦克斯韦尔空军基地中队军官学校","major":"中队军官学校课程"} ❌（原文明确说"2000年"，不应该留空）
- {"time":"","school":"空军指挥与参谋学院","major":"函授课程"} ❌（原文明确说"2004年"，不应该留空）
- {"time":"","school":"空军战争学院","major":"函授课程"} ❌（原文明确说"2012年"，不应该留空）

正确示例✅（提取原文明确的时间）：
正确提取：
- {"time":"2000","school":"麦克斯韦尔空军基地中队军官学校","major":"中队军官学校课程"}（原文明确说"2000年"）
- {"time":"2004","school":"空军指挥与参谋学院","major":"空军指挥与参谋学院课程"}（原文明确说"2004年"）
- {"time":"2012","school":"空军战争学院","major":"空军战争学院课程"}（原文明确说"2012年"）

**关键要点**：即使是函授课程、培训课程等非学位教育，如果原文中有明确的时间标注（如"2000年："、"2004年："），也必须提取出来！

**具体案例：**
原文："1982–1985年：内布拉斯加州霍尔德雷奇第195装甲团第1营，装甲兵"
- ❌ 错误提取：{"time":"1982-01至1985-01",...}（自动添加了-01）
- ✅ 正确提取：{"time":"1982至1985",...}（保持原文精度，只有年份）

原文："1990–1991年：亚利桑那州瓦丘卡堡美国陆军情报中心与学校，军事情报高级课程学员"
- ❌ 错误提取：{"time":"1990-01至1991-01",...}（自动添加了-01）
- ✅ 正确提取：{"time":"1990至1991",...}（保持原文精度，只有年份）

## 出生日期和年龄提取规则（非常重要）：
**绝对禁止推测出生日期，只能从原文直接提取！**

### birth_date（出生日期）
- ✅ 原文明确提到"1979年6月15日出生" → 提取为 "1979-06-15"
- ✅ 原文明确提到"1979年出生" → 提取为 "1979"
- ❌ 原文没有提到出生日期，但有"1997年大学毕业" → 不要推测为 "1979" 或 "1975"
- ❌ 原文只说"46岁" → 不要计算为 "1979"
- ⚠️ **如果原文没有明确的出生日期信息，必须使用 null**

### age（年龄）
- ✅ 原文明确提到"46岁" → 提取为 46
- ✅ 原文明确提到"1979年出生" → 可以计算为 2025-1979=46
- ❌ 原文没有出生日期和年龄 → 不要推测，使用 null
- ❌ 原文只有教育、工作经历 → 不要根据这些推测年龄

**重要：宁可缺失，也不要推测！**

**错误示例：**
- 原文："1994年毕业于爱荷华大学"
- ❌ 错误推测：假设22岁毕业，推测birth_date为"1972"
- ✅ 正确做法：birth_date: null, age: null

**正确示例：**
- 原文："1979年6月15日出生，现年46岁"
- ✅ 正确提取：birth_date: "1979-06-15", age: 46

## 任职日期提取规则（非常重要）：
**position_start_date必须从work_experience中提取！**

### 提取逻辑
1. **查找最新的工作记录**：
   - 优先查找time字段包含"至今"的记录
   - 如果没有"至今"，选择时间最晚的那条记录
   
2. **提取开始时间**：
   - 从time字段中提取**开始时间**（不是结束时间）
   - 保持原文精度（YYYY、YYYY-MM、YYYY-MM-DD）
   
3. **特殊情况**：
   - 如果work_experience为空，position_start_date使用null
   - 如果无法确定哪条是最新记录，选择最后一条

### 示例

**示例1（有"至今"）：**
工作经历：
- 2020-01至今：美国战略司令部，能力与资源整合主任
- 2019-01至2020-01：美国战略司令部，联合演习主任
- 2014-01至2019-01：美国战略司令部，计划与政策副主任
正确提取：position_start_date: "2020-01"（从"2020-01至今"中提取开始时间）

**示例2（只有年份精度）：**
工作经历：
- 2023至今：空军生命周期管理中心移动性项目执行官，俄亥俄州赖特-帕特森空军基地
- 2020至2023：空军物资司令部副司令
正确提取：position_start_date: "2023"（保持年份精度，不添加月份）

**示例3（没有"至今"，选最晚）：**
工作经历：
- 2020-06至2022-12：第五空军司令官
- 2018-01至2020-05：驻日美军副司令
- 2015-06至2017-12：太平洋空军参谋长
正确提取：position_start_date: "2020-06"（选择时间最晚的记录，提取开始时间）

**错误示例：**
❌ 错误1：work_experience有记录，但position_start_date为null（遗漏提取）
❌ 错误2：提取了结束时间而不是开始时间
❌ 错误3：自动添加了月份（"2023" → "2023-01"）

## 教育经历提取规则（非常重要）：

### 1. education_experience（教育经历数组）- 记录所有教育
**必须记录所有教育经历，一条不漏：**
- ✅ 学位教育（学士、硕士、博士）
- ✅ 培训课程（军校培训、领导力培训等）
- ✅ 证书课程（证书项目、专业证书等）
- ✅ 短期课程（研讨会、讲习班等）
- ✅ 函授课程
- ✅ 在线课程
- ✅ 任何形式的教育或培训活动

**示例：**
如果原文有15条教育记录，education_experience数组就应该有15条记录，不能遗漏。

### 2. education_level 和 graduate_school - 只选择最高学位
**必须严格遵守以下规则提取graduate_school和education_level：**

1. **只选择正式学位**：
   - ✅ 必须包含"学位"字样：学士学位、硕士学位、博士学位
   - ✅ 或英文：Bachelor's Degree、Master's Degree、PhD/Doctorate
   - ❌ "优秀毕业生"、"毕业生"不是学位，不能选择
   - ❌ 军校课程（Squadron Officer School、Air Command and Staff College等）的"毕业"不是学位
   - ❌ **绝对禁止创造不存在的学位名称**：只能从原文中逐字提取学位名称，不能自己编造或修改
   - ❌ **绝对禁止混淆示例和实际内容**：不要将提示词中的示例信息（如"国家安全与资源战略硕士学位"）用于当前简介
   
2. **学历等级排序**：博士学位 > 硕士学位 > 学士学位

3. **时间优先**：在相同学历等级中，选择**获得时间最晚（最新）**的学位

4. **必须匹配**：graduate_school（毕业院校）必须是获得education_level（最高学历）的那个院校

5. **排除非学位教育**：
   - ❌ 培训班、培训课程、培训项目
   - ❌ 研讨会、讲习班
   - ❌ **研修班、高级研修班**（不是学位，只是短期培训）
   - ❌ **入职培训、领导者培训**（不是学位）
   - ❌ 短期课程、进修课程
   - ❌ 函授课程（除非明确授予学位）
   - ❌ 军校短期培训（Squadron Officer School、Air Command and Staff College等）
   - ❌ "优秀毕业生"、"Distinguished Graduate"等荣誉称号
   - ❌ 任何不包含"学位/Degree"字样的教育经历
   - ❌ **特别注意**：哈佛、MIT等名校的短期培训项目也不是学位

**示例1：**
教育经历：
- 2008年：恩布里-里德尔航空大学，航空科学硕士学位
- 2015年：空军战争学院，战略研究硕士学位
- 2020年：机动部队指挥官培训课程（短期培训）

正确提取：
- education_level: "战略研究硕士学位"（2015年，最新的硕士学位）
- graduate_school: "空军战争学院"（对应战略研究硕士学位的院校）

**示例2：**
教育经历：
- 1997年：韦伯斯特大学，工商管理硕士学位
- 2007年：海军研究生院，工商管理硕士学位
- 2010年：国防大学，国家安全与资源战略硕士学位

正确提取：
- education_level: "国家安全与资源战略硕士学位"（2010年，最新的硕士学位）
- graduate_school: "国防大学"（对应2010年学位的院校）

**示例2-B（仅有一个学位）：**
教育经历：
- 1994年：美国空军学院，生物学学士学位
- 1999年：中队军官学校（培训课程，不是学位）
- 2002年：韦伯斯特大学，计算机资源与信息管理硕士学位
- 2004年：空军指挥与参谋学院课程（函授，不是学位）
- 2012年：国家战争学院课程（不是学位，只是课程）

正确提取：
- education_level: "计算机资源与信息管理硕士学位"（2002年，唯一的硕士学位）
- graduate_school: "韦伯斯特大学"（对应该硕士学位的院校）
- ❌ 错误：不要提取"国家战争学院课程"作为学位，它不包含"学位"字样
- ❌ 错误：不要创造不存在的学位名称，如"国家资源管理硕士学位"

**示例3：**
教育经历：
- 2000年：北京大学，法学硕士学位
- 2008年：清华大学，工程学博士学位
- 2015年：哈佛大学，高级管理人员培训

正确提取：
- education_level: "工程学博士学位"（博士学位高于硕士，即使不是最新的）
- graduate_school: "清华大学"（对应博士学位的院校）

**示例4（重要）：**
教育经历：
- 1994年：麻省理工学院，航空与航天学学士学位
- 1995年：斯坦福大学，航空航天工程硕士学位
- 2001年：中队军官学校，优秀毕业生
- 2007年：空军司令部参谋学院，优秀毕业生
- 2012年：武装部队工业学院，优秀毕业生

正确提取：
- education_experience: 应包含所有5条记录（学士、硕士、3个培训课程）
- education_level: "航空航天工程硕士学位"（最高学位，"优秀毕业生"不是学位）
- graduate_school: "斯坦福大学"（对应最高学位的院校）

**示例5（完整教育经历）：**
教育经历：
- 1994年：爱荷华大学，经济学学士学位
- 1996年：惠特沃思学院，国际管理硕士学位
- 2006年：国防语言学院，高级法语课程
- 2008年：乔治梅森大学，公共政策博士学位
- 2011年：空军战争学院，函授课程
- 2021年：哈佛肯尼迪学院，政府数字化转型课程
- 2022年：芝加哥大学，人工智能与数据科学课程

正确提取：
- education_experience: 应包含所有7条记录（不能遗漏任何一条）
- education_level: "公共政策博士学位"（博士是最高学位）
- graduate_school: "乔治梅森大学"（对应博士学位的院校）

**示例6（名校研修班陷阱）：**
教育经历：
- 1983年：马萨诸塞大学达特茅斯分校，电气工程学士学位
- 1991年：西新英格兰学院，工程管理硕士学位
- 2012年：麦克斯韦尔空军基地航空战争学院，函授课程
- 2019年：华盛顿特区，高级行政服务入职培训项目
- 2019年：华盛顿特区，高级领导者培训课程
- 2021年：哈佛大学约翰·F·肯尼迪政府学院，国家与国际安全项目高级研修班

正确提取：
- education_experience: 应包含所有6条记录
- education_level: "工程管理硕士学位"（1991年，唯一的硕士学位，是最高学位）
- graduate_school: "西新英格兰学院"（对应工程管理硕士学位的院校）
- ❌ 错误：不要选择"国家与国际安全项目高级研修班"，虽然是哈佛大学且时间最新，但它不是学位，只是研修班
- ❌ 错误：不要因为哈佛大学的名气而选择它，哈佛在此只提供研修班，没有授予学位
- ❌ 错误：不要选择"入职培训"或"培训课程"，它们都不是学位

- 未知字段使用null`
          : `Extract structured information from this military personnel biography and return ONLY valid JSON (no explanations).
${contextHintEn}
Biography:
${processedBiography}

**JSON Format Requirements (CRITICAL):**
- Must return strictly valid JSON format
- All field names must be enclosed in double quotes (e.g., "birth_date", NOT birth_date")
- String values must use double quotes
- Numeric values should NOT use quotes
- Do NOT add comma after the last field
- Ensure all brackets are properly paired

Return JSON format:
{
  "country_region": "Country/Region",
  "chinese_name": "Chinese name (if available)",
  "english_name": "English name",
  "gender": "male/female/unknown",
  "birth_date": "Date format (YYYY if only year, YYYY-MM if year-month, YYYY-MM-DD if complete)",
  "age": number,
  "email": "email",
  "phone": "phone",
  "graduate_school": "The graduate school corresponding to the highest degree",
  "education_level": "Highest degree",
  "military_branch": "Branch (Army/Navy/Air Force/Space Force/Marines)",
  "military_rank": "Rank",
  "organization": "Current organization",
  "current_position": "Current position",
  "position_start_date": "Date format (Start date of current position, extract from the most recent work_experience record, YYYY if only year, YYYY-MM if year-month, YYYY-MM-DD if complete)",
  "work_status": "active/retired/reserve",
  "work_experience": [{"time":"Date range (KEEP ORIGINAL PRECISION, e.g., if original is '1994-06 to 1997-06' write '1994-06 to 1997-06', if original is '1994 to 1997' write '1994 to 1997'; if no explicit time info in original text, use empty string \"\")","organization":"","position":""}],
  "education_experience": [{"time":"Date range (KEEP ORIGINAL PRECISION, e.g., if original is '1979 to 1983' write '1979 to 1983', if original is '1979-06 to 1983-05' write '1979-06 to 1983-05'; if no explicit time info in original text, use empty string \"\")","school":"","major":"Degree/Course/Training/Certificate"}]
}

**Important Note:**
- **education_experience (Education History Array)**: Must include **ALL** education records, including degrees, training, courses, certificates, seminars, etc. DO NOT omit any education record
- **education_level and graduate_school**: Only select the **highest formal degree** (follow rules below)
- **birth_date and age**: Can ONLY be **directly extracted** from the original text. If the original text does not explicitly mention birth date or age, MUST use null. **NEVER infer from education history, work experience, etc.**
- **position_start_date (Position Start Date)**: MUST extract from **work_experience**. Find the most recent work record (usually "to present" or latest time), extract its **start time**, keep original precision

# Important Instructions:

## Date Format (CRITICAL):
**Strictly KEEP the original date precision, NEVER automatically add months or days!**

- Keep date precision as in original text, do NOT auto-complete
- Use "YYYY" for year only (e.g., 1979)
- Use "YYYY-MM" for year-month (e.g., 1979-06)
- Use "YYYY-MM-DD" for complete date (e.g., 1979-06-15)
- **If no explicit time info in original text, use empty string "", NEVER guess or fabricate dates**

**For work_experience and education_experience time field (VERY IMPORTANT):**
- ❌ WRONG: Original "1985–1990", extracted as "1985-01 to 1990-01" (auto-added months)
- ✅ CORRECT: Original "1985–1990", should extract as "1985 to 1990" (keep original precision)
- ❌ WRONG: Original "June 1994 to June 1997", extracted as "1994-06-01 to 1997-06-01" (auto-added days)
- ✅ CORRECT: Original "June 1994 to June 1997", should extract as "1994-06 to 1997-06" (keep original precision)
- ❌ WRONG: Original "graduated from Harvard University" (no time info), extracted as "1990 to 1994" (fabricated)
- ✅ CORRECT: Original "graduated from Harvard University" (no time info), should extract as "" (don't guess)
- Date range format: start_time to end_time (e.g., 1994-06 to 1997-06, 1985 to 1990, 2024-04 to present)
- If still active/current, use "to present" for end time (e.g., 2024-04 to present, 2020 to present)
- **If original has no month, DO NOT add month; if original has no day, DO NOT add day**

## Work Experience Extraction Rules (CRITICAL):
**Must distinguish between "current position" and "previous positions", DO NOT mark all positions as "to present"!**

**STRICTLY FORBID timeline-based guessing (most common error):**
- ❌ Do NOT assume early work started in "1994" just because text mentions "enlisted in 1994"
- ❌ Do NOT assume later positions started in "2003" just because text mentions "became officer in 2003"
- ❌ Do NOT guess position timing based on nearby time references in the biography
- ✅ Each work experience MUST have **direct, explicit time description** to fill in time field
- ✅ If text only says "served as", "worked at" without time, time should be empty string ""

1. **Current position indicators** (only these cases should use "to present"):
   - Explicitly says "to present", "current", "assumed", "serves as" (present tense)
   - Example: "assumed position of Master Chief Petty Officer of the Thirteenth District in July 2024" → time: "2024-07 to present"

2. **Previous position indicators** (these cases should NOT use "to present"):
   - Explicitly says "previously", "served as", "former", "held positions"
   - Example: "Previously served as the master chief petty officer of Joint Interagency Task Force South" → time: "" (no explicit time)
   - Example: "held various positions including..." → time: "" (no explicit time)

3. **Time information handling**:
   - With explicit time range: "served from 1994 to 1997..." → time: "1994 to 1997"
   - With start time, no end time, and is current position: "assumed in July 2024..." → time: "2024-07 to present"
   - **With only start time, no end time, and is NOT current position**: "appointed in 2007..." → time: "2007" (do NOT guess end time)
   - No explicit time info: leave time as empty string ""
   - **NEVER guess or fabricate dates**
   - **If the text does not specify an end time, do NOT automatically guess or add an end time**

**Wrong Example❌:**
Biography: "Master Chief Petty Officer Fraughton assumed the position of Master Chief Petty Officer of the Thirteenth District in July 2024. He previously served as the master chief petty officer of Joint Interagency Task Force South and has held various positions including Training Center Cape May, Sector Charleston..."

Wrong extraction:
- Thirteenth District → time: "2024-07 to present" ✓ (correct)
- Joint Interagency Task Force South → time: "2024-07 to present" ❌ (wrong! "previously" means past, no explicit time)
- Training Center Cape May → time: "2024-07 to present" ❌ (wrong! "held positions" means past, no explicit time)

**Correct Example✅:**
Correct extraction:
- {"time":"2024-07 to present","organization":"Thirteenth District","position":"Master Chief Petty Officer"}
- {"time":"","organization":"Joint Interagency Task Force South","position":"Master Chief Petty Officer"}
- {"time":"","organization":"Training Center Cape May","position":"Commanding Officer"}
- {"time":"","organization":"Sector Charleston","position":"Commanding Officer"}

**Example 2 (Only start time, no end time):**
Biography: "In 2007, he was appointed as the Engineering Officer of the Watch. His shore duty assignments include: serving at Mid-Atlantic Regional Maintenance Center (MARMC); serving as the Operational Test Director for AEGIS systems at Operational Test and Evaluation Force (OPTEVFOR)..."

Wrong extraction❌:
- {"time":"2007 to present","organization":"...","position":"Engineering Officer of the Watch"} ❌ (NOT current position, should NOT use "to present")
- {"time":"2007 to 2010","organization":"Mid-Atlantic Regional Maintenance Center","position":"..."} ❌ (guessed end time)

Correct extraction✅:
- {"time":"2007","organization":"...","position":"Engineering Officer of the Watch"} (only start time)
- {"time":"","organization":"Mid-Atlantic Regional Maintenance Center","position":"..."} (no explicit time)
- {"time":"","organization":"Operational Test and Evaluation Force","position":"Operational Test Director for AEGIS systems"} (no explicit time)

**Example 3 (Timeline-based guessing error):**
Biography: "Captain Rochelle's naval career began in September 1994 as an enlisted Sailor. ...Afterward, he served aboard USS KINKAID (DD 965) as a Fire Controlman for Tomahawk Missiles. In May 2003, Captain Rochelle received a Bachelor of Science degree in Electrical Engineering from Auburn University and was commissioned as a naval officer through the Seaman-to-Admiral program. He subsequently served as Electrical Officer aboard USS BOONE (FFG 28) and as Fire Control Officer aboard USS VICKSBURG (CG 69)."

Wrong extraction❌ (timeline-based guessing):
- {"time":"1994-09 to 2003-05","organization":"USS KINKAID","position":"Fire Controlman for Tomahawk Missiles"} ❌ (text only says "served aboard", no time! Do NOT guess based on "1994 enlisted" and "2003 commissioned")
- {"time":"2003-05","organization":"USS BOONE","position":"Electrical Officer"} ❌ (text only says "served as", no time!)
- {"time":"2003-05","organization":"USS VICKSBURG","position":"Fire Control Officer"} ❌ (text only says "served as", no time!)

Correct extraction✅:
- {"time":"","organization":"USS KINKAID","position":"Fire Controlman for Tomahawk Missiles"} (text only says "served", no time)
- {"time":"","organization":"USS BOONE","position":"Electrical Officer"} (text only says "served as", no time)
- {"time":"","organization":"USS VICKSBURG","position":"Fire Control Officer"} (text only says "served as", no time)

**Key point**: Even though the text mentions "September 1994 enlisted" and "May 2003 became officer", these **CANNOT** be used to guess other work experience timing!

**Example 4 (Multiple date confusion - CRITICAL):**
Biography: "Rear Admiral Trenor is the current commander of Defense Logistics Agency Weapon Support (Columbus division). The division was established on October 1, 2025. Before assuming her current position on July 28, 2025, Rear Admiral Trenor served as the Fleet Ordnance and Supply Director at U.S. Fleet Forces Command."

**Key Information**:
- "assumed position on July 28, 2025" ← Personal assignment date
- "division was established on October 1, 2025" ← Organization establishment date (NOT related to personal assignment)

Wrong extraction❌ (confused personal assignment date with organization establishment date):
- {"time":"2025-10 to present","organization":"Defense Logistics Agency Weapon Support","position":"Commander"} ❌ (WRONG! Used division establishment date instead of assignment date)

Correct extraction✅ (use personal assignment date):
- {"time":"2025-07 to present","organization":"Defense Logistics Agency Weapon Support","position":"Commander"} (use "assumed on July 28, 2025")
- {"time":"","organization":"U.S. Fleet Forces Command","position":"Fleet Ordnance and Supply Director"} (only says "served as", no explicit time)

**Time Extraction Priority Rules (CRITICAL)**:
When multiple dates exist in the original text, must strictly distinguish:
1. ✅ **Personal assignment related dates** (HIGHEST PRIORITY):
   - "assumed position on July 28, 2025" → use 2025-07
   - "serving since 2020" → use 2020
   - "took office in January 2024" → use 2024-01
   
2. ❌ **Organization/institution related dates** (NEVER USE):
   - "division was established on October 1, 2025" ← This is organization establishment date, NOT personal assignment date
   - "began operations in 2026" ← This is organization operation date, NOT personal assignment date
   - "unit reorganized in 2020" ← This is organization change date, NOT personal assignment date

**How to Judge**:
- Check who is the subject of the time phrase:
  - "John Smith assumed position in July 2025..." ✅ (subject is John Smith, personal assignment date)
  - "The division was established in October 2025..." ❌ (subject is division, organization date)
  - "The unit reorganized in 2020..." ❌ (subject is unit, organization date)

## Education Experience Extraction Rules (CRITICAL):
**NEVER guess or fabricate education dates, if no explicit time info in original text, use empty string "" for time field**

**STRICTLY FORBID timeline-based guessing (most common error):**
- ❌ Do NOT assume early training started in "1994" just because text mentions "enlisted in 1994"
- ❌ Do NOT assume training ended in "2003" or "1997" just because text mentions "graduated in 2003"
- ❌ Do NOT guess education timing based on nearby time references in the biography
- ✅ Each education record MUST have **direct, explicit time description** to fill in time field
- ✅ If text only says "completed training", "attended school" without time, time should be empty string ""

**Wrong Example❌ (fabricating dates):**
Biography: "Graduated from the College of Charleston. His academic degrees include a Bachelor of Science in Finance and a Master's in Strategic Leadership. He is also a graduate of the U.S. Army Sergeants Major Academy..."

Wrong extraction:
- College of Charleston, Bachelor of Science in Finance → time: "1994 to 1997" ❌ (no time in original, fabricated)
- College of Charleston, Master's in Strategic Leadership → time: "1994 to 1997" ❌ (no time in original, fabricated)

**Wrong Example❌ (timeline-based guessing):**
Biography: "Captain Rochelle's naval career began in September 1994 as an enlisted Sailor. He completed recruit training at Great Lakes, Illinois. He then attended Fire Controlman 'A' School, followed by transfer to Tomahawk Weapon System 'C' School in San Diego, California. In May 2003, Captain Rochelle received a Bachelor of Science degree in Electrical Engineering from Auburn University. Captain Rochelle also earned a Master of Science degree in Applied Physics from the Naval Postgraduate School."

Wrong extraction (timeline-based guessing):
- {"time":"1994-09 to 1997-06","school":"Great Lakes Recruit Training","major":"Recruit Training"} ❌ (text only says "completed training", no time! Do NOT guess based on "September 1994 enlisted")
- {"time":"1994-09 to 1997-06","school":"Fire Controlman 'A' School","major":"..."} ❌ (text only says "attended school", no time!)
- {"time":"1994-09 to 1997-06","school":"Tomahawk Weapon System 'C' School","major":"..."} ❌ (text only says "transfer to", no time!)
- {"time":"2025","school":"Naval Postgraduate School","major":"Master of Science in Applied Physics"} ❌ (text only says "earned", no time!)

**Key point**: Even though the text mentions "September 1994 enlisted" and "May 2003 graduated", these **CANNOT** be used to guess other education timing!

**Correct Example✅ (same biography, correct extraction):**
Correct extraction:
- {"time":"","school":"Great Lakes Recruit Training","major":"Recruit Training"} (text only says "completed", no time)
- {"time":"","school":"Fire Controlman 'A' School","major":"Fire Controlman Training"} (text only says "attended", no time)
- {"time":"","school":"Tomahawk Weapon System 'C' School","major":"Tomahawk Weapon System Training"} (text only says "transfer to", no time)
- {"time":"2003-05","school":"Auburn University","major":"Bachelor of Science in Electrical Engineering"} (text explicitly says "In May 2003 received")
- {"time":"","school":"Naval Postgraduate School","major":"Master of Science in Applied Physics"} (text only says "earned", no time)

**Comparison explanation**:
- "September 1994 enlisted" is enlistment time, NOT recruit training time
- Recruit training, A School, C School all have NO direct time description, so time is empty string
- Only Auburn University has explicit "May 2003", so it gets the time filled in

**Correct Example with complete time info:**
Biography: "Attended the United States Naval Academy from 1979 to 1983, earning a Bachelor of Science degree"
Correct extraction:
- {"time":"1979 to 1983","school":"United States Naval Academy","major":"Bachelor of Science"}

**CRITICAL: Correspondence courses and training courses also need time extraction (if explicitly stated in original text)**
Wrong Example❌ (missing time):
Biography: "2000: Completed Squadron Officer School course at Maxwell Air Force Base via correspondence. 2004: Completed Air Command and Staff College via correspondence. 2012: Completed Air War College via correspondence"

Wrong extraction (ignored time):
- {"time":"","school":"Squadron Officer School, Maxwell Air Force Base","major":"Squadron Officer School course"} ❌ (original explicitly says "2000", should not be empty)
- {"time":"","school":"Air Command and Staff College","major":"correspondence course"} ❌ (original explicitly says "2004", should not be empty)
- {"time":"","school":"Air War College","major":"correspondence course"} ❌ (original explicitly says "2012", should not be empty)

Correct Example✅ (extract explicit time from original text):
Correct extraction:
- {"time":"2000","school":"Squadron Officer School, Maxwell Air Force Base","major":"Squadron Officer School course"} (original explicitly says "2000")
- {"time":"2004","school":"Air Command and Staff College","major":"Air Command and Staff College course"} (original explicitly says "2004")
- {"time":"2012","school":"Air War College","major":"Air War College course"} (original explicitly says "2012")

**Key Point**: Even for correspondence courses, training courses, and other non-degree education, if the original text has explicit time notation (like "2000:", "2004:"), it MUST be extracted!

**Specific Examples:**
Original: "1982–1985: 195th Armored Regiment, 1st Battalion, Holdrege, Nebraska, Armor Officer"
- ❌ WRONG: {"time":"1982-01 to 1985-01",...} (auto-added -01)
- ✅ CORRECT: {"time":"1982 to 1985",...} (keep original precision, year only)

Original: "1990–1991: U.S. Army Intelligence Center and School, Fort Huachuca, Arizona, Military Intelligence Advanced Course Student"
- ❌ WRONG: {"time":"1990-01 to 1991-01",...} (auto-added -01)
- ✅ CORRECT: {"time":"1990 to 1991",...} (keep original precision, year only)

## Birth Date and Age Extraction Rules (CRITICAL):
**NEVER infer birth date, ONLY extract directly from original text!**

### birth_date (Birth Date)
- ✅ Original explicitly says "born June 15, 1979" → extract as "1979-06-15"
- ✅ Original explicitly says "born in 1979" → extract as "1979"
- ❌ Original doesn't mention birth date but has "graduated in 1997" → DO NOT infer as "1979" or "1975"
- ❌ Original only says "46 years old" → DO NOT calculate as "1979"
- ⚠️ **If original text has NO explicit birth date information, MUST use null**

### age (Age)
- ✅ Original explicitly says "46 years old" → extract as 46
- ✅ Original explicitly says "born in 1979" → can calculate as 2025-1979=46
- ❌ Original has NO birth date or age → DO NOT infer, use null
- ❌ Original only has education/work history → DO NOT infer age from these

**Important: Better to miss than to guess!**

**Wrong Example:**
- Original: "Graduated from University of Iowa in 1994"
- ❌ WRONG inference: Assume 22 years old at graduation, infer birth_date as "1972"
- ✅ CORRECT approach: birth_date: null, age: null

**Correct Example:**
- Original: "Born June 15, 1979, currently 46 years old"
- ✅ CORRECT extraction: birth_date: "1979-06-15", age: 46

## Position Start Date Extraction Rules (CRITICAL):
**position_start_date MUST be extracted from work_experience!**

### Extraction Logic
1. **Find the most recent work record**:
   - Prioritize records with time field containing "to present"
   - If no "to present", select the record with the latest time
   
2. **Extract start time**:
   - Extract the **start time** from the time field (not the end time)
   - Keep original precision (YYYY, YYYY-MM, YYYY-MM-DD)
   
3. **Special cases**:
   - If work_experience is empty, position_start_date should be null
   - If cannot determine which is the most recent, choose the last one

### Examples

**Example 1 (with "to present"):**
Work experience:
- 2020-01 to present: U.S. Strategic Command, Director for Capability and Resource Integration
- 2019-01 to 2020-01: U.S. Strategic Command, Director for Joint Exercise
- 2014-01 to 2019-01: U.S. Strategic Command, Deputy Director for Plans and Policy
Correct extraction: position_start_date: "2020-01" (extract start time from "2020-01 to present")

**Example 2 (year precision only):**
Work experience:
- 2023 to present: Program Executive Officer and Director for the Mobility Directorate, Air Force Life Cycle Management Center, Wright-Patterson AFB
- 2020 to 2023: Deputy Commander, Air Force Materiel Command
Correct extraction: position_start_date: "2023" (keep year precision, do not add month)

**Example 3 (no "to present", select latest):**
Work experience:
- 2020-06 to 2022-12: Commander, Fifth Air Force
- 2018-01 to 2020-05: Deputy Commander, U.S. Forces Japan
- 2015-06 to 2017-12: Chief of Staff, Pacific Air Forces
Correct extraction: position_start_date: "2020-06" (select record with latest time, extract start time)

**Wrong Examples:**
❌ Wrong 1: work_experience has records, but position_start_date is null (missed extraction)
❌ Wrong 2: Extracted end time instead of start time
❌ Wrong 3: Auto-added month ("2023" → "2023-01")

## Education History Extraction Rules (CRITICAL):

### 1. education_experience (Education History Array) - Record ALL Education
**Must record ALL education history, not a single one should be omitted:**
- ✅ Degree programs (Bachelor's, Master's, PhD)
- ✅ Training courses (military training, leadership training, etc.)
- ✅ Certificate programs (professional certificates, etc.)
- ✅ Short courses (seminars, workshops, etc.)
- ✅ Correspondence courses
- ✅ Online courses
- ✅ Any form of education or training activity

**Example:**
If the original text has 15 education records, the education_experience array should have 15 entries. DO NOT omit any.

### 2. education_level and graduate_school - Select Only Highest Degree
**MUST strictly follow these rules for extracting graduate_school and education_level:**

1. **Only Select Formal Degrees**:
   - ✅ MUST contain "Degree": Bachelor's Degree, Master's Degree, PhD/Doctorate
   - ✅ Or in Chinese: 学士学位、硕士学位、博士学位
   - ❌ "Distinguished Graduate", "Graduate" are NOT degrees, DO NOT select
   - ❌ Military school courses (Squadron Officer School, Air Command and Staff College, etc.) "graduation" is NOT a degree
   
2. **Degree Hierarchy**: PhD/Doctorate > Master's Degree > Bachelor's Degree

3. **Time Priority**: Among degrees of the same level, choose the **most recent (latest date)**

4. **Must Match**: graduate_school MUST be the institution where the education_level was obtained

5. **Exclude Non-Degree Programs**:
   - ❌ Training courses/programs
   - ❌ Workshops, seminars
   - ❌ Short-term courses, professional development courses
   - ❌ Correspondence courses (unless explicitly awarding a degree)
   - ❌ Military short-term training (Squadron Officer School, Air Command and Staff College, etc.)
   - ❌ "Distinguished Graduate", "Honor Graduate" are honorary titles, NOT degrees
   - ❌ Any education that does NOT contain "Degree/学位"

**Example 1:**
Education history:
- 2008: Embry-Riddle Aeronautical University, Master of Aeronautical Science
- 2015: Air War College, Master of Strategic Studies
- 2020: Director of Mobility Forces Course (short-term training)

Correct extraction:
- education_level: "Master of Strategic Studies" (2015, most recent master's degree)
- graduate_school: "Air War College" (institution for the Strategic Studies degree)

**Example 2:**
Education history:
- 1997: Webster University, MBA
- 2007: Naval Postgraduate School, MBA
- 2012: National Defense University, Master of National Security and Resource Strategy

Correct extraction:
- education_level: "Master of National Security and Resource Strategy" (2012, most recent master's)
- graduate_school: "National Defense University" (institution for 2012 degree)

**Example 3:**
Education history:
- 2000: Peking University, Master of Law
- 2008: Tsinghua University, PhD in Engineering
- 2015: Harvard University, Executive Leadership Program

Correct extraction:
- education_level: "PhD in Engineering" (PhD is higher than Master's, even if not the most recent)
- graduate_school: "Tsinghua University" (institution for the PhD)

**Example 4 (IMPORTANT):**
Education history:
- 1994: Massachusetts Institute of Technology, Bachelor's Degree in Aeronautics and Astronautics
- 1995: Stanford University, Master's Degree in Aeronautical Engineering
- 2001: Squadron Officer School, Distinguished Graduate
- 2007: Air Command and Staff College, Distinguished Graduate
- 2012: Armed Forces Industrial College, Distinguished Graduate

Correct extraction:
- education_experience: Must include all 5 records (Bachelor's, Master's, 3 training courses)
- education_level: "Master's Degree in Aeronautical Engineering" (highest degree, "Distinguished Graduate" is NOT a degree)
- graduate_school: "Stanford University" (institution for the highest degree)

**Example 5 (Complete Education History):**
Education history:
- 1994: University of Iowa, Bachelor's Degree in Economics
- 1996: Whitworth College, Master's Degree in International Management
- 2006: Defense Language Institute, Advanced French Course
- 2008: George Mason University, PhD in Public Policy
- 2011: Air War College, correspondence course
- 2021: Harvard Kennedy School, Digital Transformation in Government
- 2022: University of Chicago, AI and Data Science Course

Correct extraction:
- education_experience: Must include all 7 records (DO NOT omit any)
- education_level: "PhD in Public Policy" (PhD is the highest degree)
- graduate_school: "George Mason University" (institution for the PhD)

- Use null for unknown fields`;



        const response = await axios.post(
          config.llm.apiUrl,
          {
            model: config.llm.model,
            messages: [
              {
                role: 'user',
                content: prompt
              }
            ],
            temperature: 0  // 降到0以获得更确定性和更快的响应
            // 不限制max_tokens，让LLM输出完整信息
          },
          {
            headers: {
              'Authorization': `Bearer ${config.llm.apiToken}`,
              'Content-Type': 'application/json'
            },
            timeout: 360000  // 增加到 360 秒（6分钟），以处理完整的长简介
          }
        );

        const content = response.data.choices[0].message.content;
        
        // 尝试解析JSON
        let extractedData;
        try {
          // 移除可能的markdown代码块标记
          let jsonStr = content.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim();
          
          // 第一次尝试：直接解析
          try {
            extractedData = JSON.parse(jsonStr);
          } catch (firstError) {
            // 第二次尝试：修复常见错误后再解析
            console.log('⚠️  JSON解析失败，尝试自动修复...');
            const fixedJsonStr = this.fixCommonJsonErrors(jsonStr);
            
            try {
              extractedData = JSON.parse(fixedJsonStr);
              console.log('✓ JSON自动修复成功');
            } catch (secondError) {
              // 修复后仍然失败，输出详细错误信息
              console.error('JSON解析失败，原始内容:', content);
              console.error('修复后内容:', fixedJsonStr);
              console.error('解析错误:', secondError.message);
              throw new Error('LLM返回的数据格式不正确');
            }
          }
        } catch (parseError) {
          throw parseError;
        }

        // 成功返回
        if (attempt > 1) {
          console.log(`✓ 第 ${attempt} 次尝试成功`);
        }
        return extractedData;

      } catch (error) {
        lastError = error;
        const isTimeout = error.code === 'ECONNABORTED' || error.message.includes('timeout');
        const isNetworkError = error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT';
        
        console.error(`LLM数据抽取失败 (尝试 ${attempt}/${maxRetries}):`, error.message);
        
        // 如果是超时或网络错误，且还有重试机会，则重试
        if ((isTimeout || isNetworkError) && attempt < maxRetries) {
          const waitTime = attempt * 2000; // 递增等待时间
          console.log(`等待 ${waitTime}ms 后重试...`);
          await new Promise(resolve => setTimeout(resolve, waitTime));
          continue;
        }
        
        // 最后一次尝试失败，或者不是可重试的错误
        break;
      }
    }

    // 所有尝试都失败
    console.error(`LLM数据抽取最终失败，已重试 ${maxRetries} 次`);
    throw lastError;
  }

  /**
   * 调用翻译API将文本翻译为中文
   * @param {string} text - 待翻译的文本
   * @returns {Promise<string>} - 翻译后的中文文本
   */
  async translateToChinese(text) {
    try {
      if (!text || text.trim() === '') {
        return '';
      }

      // 使用完整文本，不截断
      const response = await axios.post(
        config.translate.apiUrl,
        {
          model: config.translate.model,
          messages: [
            {
              role: 'user',
              content: `将以下段落翻译成中文，不要额外解释。\n\n${text}`
            }
          ],
          max_tokens: 8196
        },
        {
          headers: {
            'Content-Type': 'application/json'
          },
          timeout: 600000  // 增加到 600 秒（10分钟）
        }
      );

      const translatedText = response.data.choices[0].message.content.trim();
      return translatedText;
    } catch (error) {
      console.error('翻译失败:', error.message);
      return text; // 翻译失败时返回原文
    }
  }

  /**
   * 调用翻译API将文本翻译为英文
   * @param {string} text - 待翻译的文本
   * @returns {Promise<string>} - 翻译后的英文文本
   */
  async translateToEnglish(text) {
    try {
      if (!text || text.trim() === '') {
        return '';
      }

      // 使用完整文本，不截断
      const response = await axios.post(
        config.translate.apiUrl,
        {
          model: config.translate.model,
          messages: [
            {
              role: 'user',
              content: `Translate the following text to English, without additional explanation.\n\n${text}`
            }
          ]
        },
        {
          headers: {
            'Content-Type': 'application/json'
          },
          timeout: 600000  // 增加到 600 秒（10分钟）
        }
      );

      const translatedText = response.data.choices[0].message.content.trim();
      return translatedText;
    } catch (error) {
      console.error('翻译失败:', error.message);
      return text; // 翻译失败时返回原文
    }
  }

  /**
   * 检测文本语言并翻译为双语
   * @param {string} text - 待翻译的文本
   * @returns {Promise<Object>} - 包含中文和英文的对象
   */
  async translateToBothLanguages(text) {
    try {
      if (!text || text.trim() === '') {
        return { chinese: '', english: '' };
      }

      // 简单判断是否为中文（包含中文字符）
      const hasChinese = /[\u4e00-\u9fa5]/.test(text);

      if (hasChinese) {
        // 原文是中文，翻译为英文
        const english = await this.translateToEnglish(text);
        return { chinese: text, english };
      } else {
        // 原文是英文或其他语言，翻译为中文
        const chinese = await this.translateToChinese(text);
        return { chinese, english: text };
      }
    } catch (error) {
      console.error('双语翻译失败:', error.message);
      return { chinese: text, english: text };
    }
  }

  /**
   * 翻译单个字段（用于学历、军衔、军兵种、职务、工作状态等）
   * @param {string} fieldValue - 字段值
   * @returns {Promise<string>} - 翻译后的中文
   */
  async translateField(fieldValue) {
    try {
      if (!fieldValue || typeof fieldValue !== 'string') {
        return fieldValue;
      }

      // 如果已经是中文，直接返回
      if (/[\u4e00-\u9fa5]/.test(fieldValue)) {
        return fieldValue;
      }

      // 领域特定翻译映射（优先使用，避免通用翻译的歧义）
      const domainSpecificMap = {
        // 工作状态
        'active': '在役',
        'active duty': '现役',
        'retired': '退役',
        'reserve': '预备役',
        'inactive': '非现役',
        'discharged': '退伍',
        'serving': '在职',
        'employed': '在职',
        
        // 性别
        'male': '男',
        'female': '女',
        'unknown': '未知',
        
        // 常见军衔（补充）
        'general': '将军',
        'admiral': '上将',
        'colonel': '上校',
        'captain': '上尉',
        'lieutenant': '中尉',
        'sergeant': '中士',
        'private': '列兵',
        
        // 军兵种
        'army': '陆军',
        'navy': '海军',
        'air force': '空军',
        'marine': '海军陆战队',
        'space force': '太空军',
        'coast guard': '海岸警卫队'
      };

      // 标准化字段值（小写，去除多余空格）
      const normalizedValue = fieldValue.toLowerCase().trim();
      
      // 检查是否有精确匹配的领域特定翻译
      if (domainSpecificMap[normalizedValue]) {
        return domainSpecificMap[normalizedValue];
      }

      // 检查是否包含关键词（部分匹配）
      for (const [key, value] of Object.entries(domainSpecificMap)) {
        if (normalizedValue.includes(key)) {
          return value;
        }
      }

      // 如果没有匹配到领域特定翻译，则调用通用翻译API
      return await this.translateToChinese(fieldValue);
    } catch (error) {
      console.warn(`字段翻译失败 (${fieldValue}):`, error.message);
      return fieldValue; // 翻译失败返回原值
    }
  }

  /**
   * AI审核：对比提取的数据与原文，判断准确性
   * @param {Object} extractedData - 提取后的结构化数据
   * @param {string} chineseBiography - 中文简介原文
   * @returns {Promise<Object>} - 审核结果
   */
  async aiAudit(extractedData, chineseBiography) {
    try {
      console.log('📋 开始AI审核...');

      // 格式化字段值显示
      const formatFieldValue = (value) => {
        if (value === null || value === undefined || value === '') {
          return '[空]';
        }
        return value;
      };

      const prompt = `你是一个专业的数据审核员。请对比以下提取的结构化数据和原始中文简介，判断数据提取的准确性。

# 提取的结构化数据：
- 中文姓名：${formatFieldValue(extractedData.chinese_name)}
- 英文姓名：${formatFieldValue(extractedData.english_name)}
- 性别：${formatFieldValue(extractedData.gender)}
- 出生日期：${formatFieldValue(extractedData.birth_date)}
- 年龄：${formatFieldValue(extractedData.age)}
- 邮箱：${formatFieldValue(extractedData.email)}
- 电话：${formatFieldValue(extractedData.phone)}
- 毕业院校：${formatFieldValue(extractedData.graduate_school)}
- 最高学历：${formatFieldValue(extractedData.education_level)}
- 军兵种：${formatFieldValue(extractedData.military_branch)}
- 军衔：${formatFieldValue(extractedData.military_rank)}
- 所在机构：${formatFieldValue(extractedData.organization)}
- 当前职务：${formatFieldValue(extractedData.current_position)}
- 任职日期：${formatFieldValue(extractedData.position_start_date)}
- 工作状态：${formatFieldValue(extractedData.work_status)}

# 原始中文简介：
${chineseBiography}

# 审核规则：
1. **空值不算错误**：字段为[空]表示原文未提及，这是正常的，不要标记为不准确
2. **准确性判断标准**：
   - ✅ 准确：提取的值与原文相符（即使原文有多个选项，只要提取的是其中之一就算准确）
   - ❌ 不准确：提取的值与原文明确不符，或提取了原文中不存在的信息
3. **特殊规则**：
   - 最高学历：必须是正式学位（学士、硕士、博士学位），并且是最高级别的，**必须在原文中真实存在**
   - 毕业院校：必须是获得最高学历的那个院校，**必须在原文中真实存在**
   - **最高学历或毕业院校在原文中不存在是严重错误**：必须标记为不准确并修正
   - **最高学历与毕业院校不匹配是提取错误**：如果最高学历和毕业院校不对应，需要修正为正确的值
   - **禁止幻觉**：提取的学位名称必须与原文完全一致，不能创造、修改或混淆提示词示例中的学位
   - 当前职务 = 简介中最后一个/现任职务
   - 任职日期 = 当前职务的开始日期
4. **不准确字段的判断**：
   - 必须是提取的值明确与原文冲突
   - 不包括"应该选择更好的选项"的情况
   - 不包括空值情况

# 请你完成以下任务：
1. 逐字段对比提取的数据与原文
2. **准确的字段**：提取的值在原文中能找到依据，或为合理的空值
3. **不准确的字段**：仅包括那些提取值与原文明确冲突的字段
   - 提供正确的值（从原文中提取）
   - 说明为什么不准确
4. **需要关注的问题**：
   - 重要信息缺失（如关键字段为空）
   - 日期前后矛盾
   - 其他逻辑不一致的地方（但不包括最高学历与毕业院校不匹配，那应该归入不准确字段）
5. 给出整体评分（0-100分）：
   - 基于实际错误数量，不要因为"需要关注的问题"而大幅降分
   - 空值不影响评分
6. 给出审核建议：
   - "通过"：90分以上，无明确错误或仅有需要关注的问题
   - "需要修改"：60-89分，有明确错误但可修正
   - "驳回"：60分以下，错误较多需重新处理

# 返回JSON格式：
{
  "overall_score": 整体评分（0-100的数字），
  "recommendation": "通过/需要修改/驳回",
  "accurate_fields": ["准确的字段中文名（包括合理的空值）"],
  "inaccurate_fields": [
    {
      "field": "字段中文名",
      "extracted": "当前提取的错误值",
      "should_be": "从原文中提取的正确值",
      "reason": "简短说明（不超过30字，禁止重复）"
    }
  ],
  "issues": [
    "其他需要人工关注的问题（如日期矛盾、重要信息缺失等）..."
  ],
  "summary": "审核总结（1-2句话）"
}

**JSON格式要求：**
- reason字段必须简洁，不超过30字
- 禁止在reason中重复相同内容
- 如果逻辑复杂，只写核心原因即可

# 重要规则（必须严格遵守）：

## 规则1: inaccurate_fields 的判断标准
**只有提取的值在原文中不存在或明确错误时，才能放入 inaccurate_fields**

错误示例❌（should_be和extracted相同）：
{
  "field": "毕业院校",
  "extracted": "亚利桑那大学",
  "should_be": "亚利桑那大学",  // ❌ should_be和extracted相同，这是错误的！
  "reason": "应该对应最高学历"
}

正确做法✅（should_be和extracted不同）：
{
  "field": "毕业院校",
  "extracted": "亚利桑那大学",
  "should_be": "国防大学",  // ✅ should_be和extracted不同，提供了正确的值
  "reason": "毕业院校应该是获得最高学历的院校，即国家资源管理硕士学位对应的国防大学"
}

## 规则2: inaccurate_fields 的 should_be 必须不同于 extracted
**如果你发现 should_be 和 extracted 相同，说明该字段不应该在 inaccurate_fields 中！**

- ✅ 正确：extracted: "张三", should_be: "李四"
- ❌ 错误：extracted: "亚利桑那大学", should_be: "亚利桑那大学"

## 规则3: 最高学历与毕业院校不匹配是提取错误
**如果最高学历与毕业院校不对应，说明其中一个或两个字段提取错误，应该放入 inaccurate_fields 并修正**

示例1：最高学历错误
原文教育经历：
- 1995年：亚利桑那大学，光学科学硕士学位
- 2005年：福特莱斯利·J·麦克奈尔军事学院，国家资源管理硕士学位
- 2009年：哈佛大学，高级行政研究员项目（非学位）

提取结果：
- 最高学历：高级行政研究员项目
- 毕业院校：哈佛大学

正确的审核结果：
{
  "accurate_fields": [],
  "inaccurate_fields": [
    {
      "field": "最高学历",
      "extracted": "高级行政研究员项目",
      "should_be": "国家资源管理硕士学位",
      "reason": "高级行政研究员项目不是正式学位，最高学历应该是2005年的国家资源管理硕士学位"
    },
    {
      "field": "毕业院校",
      "extracted": "哈佛大学",
      "should_be": "福特莱斯利·J·麦克奈尔军事学院",
      "reason": "应该是最高学历对应的院校，即国家资源管理硕士学位的毕业院校"
    }
  ],
  "issues": []
}

示例2：毕业院校错误
原文教育经历：
- 1995年：亚利桑那大学，光学科学硕士学位
- 2005年：国防大学，国家资源管理硕士学位（博士级别）

提取结果：
- 最高学历：国家资源管理硕士学位
- 毕业院校：亚利桑那大学

正确的审核结果：
{
  "accurate_fields": ["最高学历"],
  "inaccurate_fields": [
    {
      "field": "毕业院校",
      "extracted": "亚利桑那大学",
      "should_be": "国防大学",
      "reason": "毕业院校应该是获得最高学历的院校，即2005年国家资源管理硕士学位对应的国防大学"
    }
  ],
  "issues": []
}

示例3：AI幻觉错误（严重错误）
原文教育经历：
- 1994年：美国空军学院，生物学学士学位
- 1999年：中队军官学校（培训课程）
- 2002年：韦伯斯特大学，计算机资源与信息管理硕士学位
- 2012年：国家战争学院课程（非学位）

提取结果：
- 最高学历：国家资源管理硕士学位（❌ 原文中不存在这个学位名称）
- 毕业院校：国家战争学院（❌ 该机构在原文中没有授予学位）

正确的审核结果：
{
  "accurate_fields": [],
  "inaccurate_fields": [
    {
      "field": "最高学历",
      "extracted": "国家资源管理硕士学位",
      "should_be": "计算机资源与信息管理硕士学位",
      "reason": "原文中不存在该学位，唯一的硕士学位是2002年的计算机资源与信息管理硕士学位"
    },
    {
      "field": "毕业院校",
      "extracted": "国家战争学院",
      "should_be": "韦伯斯特大学",
      "reason": "国家战争学院在原文中只是课程，唯一授予硕士学位的是韦伯斯特大学"
    }
  ],
  "issues": []
}

示例4：名校研修班陷阱（严重错误）
原文教育经历：
- 1983年：马萨诸塞大学达特茅斯分校，电气工程学士学位
- 1991年：西新英格兰学院，工程管理硕士学位
- 2021年：哈佛大学，国家与国际安全项目高级研修班

提取结果：
- 最高学历：国家与国际安全项目高级研修班（❌ 研修班不是学位）
- 毕业院校：哈佛大学（❌ 哈佛只提供研修班，没有授予学位）

正确的审核结果：
{
  "accurate_fields": [],
  "inaccurate_fields": [
    {
      "field": "最高学历",
      "extracted": "国家与国际安全项目高级研修班",
      "should_be": "工程管理硕士学位",
      "reason": "高级研修班不是学位，唯一的硕士学位是1991年的工程管理硕士学位"
    },
    {
      "field": "毕业院校",
      "extracted": "哈佛大学",
      "should_be": "西新英格兰学院",
      "reason": "哈佛只提供研修班（非学位），授予硕士学位的是西新英格兰学院"
    }
  ],
  "issues": []
}

## 规则4: 只有明确错误才放入 inaccurate_fields

以下情况**不应该**放入 inaccurate_fields：
- ❌ 空值（这是正常的）
- ❌ 提取的值在原文中存在，且与其他字段无逻辑冲突，但"应该选更好的"

以下情况**应该**放入 inaccurate_fields：
- ✅ 姓名拼写错误：提取"张三"，原文是"李四"
- ✅ 日期错误：提取"2020"，原文是"2021"
- ✅ 职务错误：提取"主任"，原文是"副主任"
- ✅ 提取了不存在的信息：提取"博士学位"，原文只有硕士
- ✅ **最高学历与毕业院校不匹配**：说明至少有一个字段提取错误，需要修正

## 最后的自我检查（在返回结果前必须完成）：
1. 检查 inaccurate_fields 中的每一项，确保 should_be ≠ extracted
2. 检查 inaccurate_fields 中的每一项，确保 extracted 的值确实需要修正
3. **特别检查最高学历与毕业院校**：
   - 如果两者不匹配，必须将错误的字段放入 inaccurate_fields
   - 不要放入 issues，应该直接修正
4. **特别检查AI幻觉（非常重要）**：
   - 检查"最高学历"字段：提取的学位名称是否在原文中真实存在？
   - 检查"毕业院校"字段：提取的院校是否在原文中确实授予了学位（不只是课程）？
   - 如果发现创造了不存在的学位，必须标记为不准确并提供正确的学位名称
   - 绝对禁止混淆提示词示例和实际原文内容
5. 如果发现 should_be = extracted，说明该字段不应该在 inaccurate_fields 中
6. **检查reason字段**：
   - 每个reason不超过30字
   - 不要重复相同内容
   - 只写核心原因，例如："原文是X，提取为Y"

注意：只返回JSON，不要任何额外文字。`;

      const response = await axios.post(
        config.llm.apiUrl,
        {
          model: config.llm.model,
          messages: [
            {
              role: 'user',
              content: prompt
            }
          ],
          temperature: 0.3,  // 稍高的温度，获得更好的分析
          max_tokens: 4000   // 限制最大输出长度，避免过长的reason导致问题
        },
        {
          headers: {
            'Authorization': `Bearer ${config.llm.apiToken}`,
            'Content-Type': 'application/json'
          },
          timeout: 120000  // 2分钟超时
        }
      );

      const content = response.data.choices[0].message.content;
      
      // 解析JSON
      let auditResult;
      try {
        const jsonStr = content.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim();
        auditResult = JSON.parse(jsonStr);
      } catch (parseError) {
        console.error('AI审核结果JSON解析失败:', parseError.message);
        
        // 检查是否是因为reason字段过长或重复导致的
        if (content.includes('"reason":') && content.length > 10000) {
          console.error('⚠️ 检测到reason字段异常过长（可能包含重复内容）');
          console.error('响应长度:', content.length);
          console.error('前500字符:', content.substring(0, 500));
          console.error('后500字符:', content.substring(content.length - 500));
        } else {
          console.error('完整响应:', content.substring(0, 1000));
        }
        
        throw new Error('AI审核结果格式不正确');
      }

      // 后处理：验证和修正不合理的结果
      if (auditResult.inaccurate_fields && auditResult.inaccurate_fields.length > 0) {
        const validInaccurateFields = [];
        const movedToIssues = [];

        for (const field of auditResult.inaccurate_fields) {
          // 检查1: should_be 和 extracted 是否相同
          if (field.should_be === field.extracted) {
            console.warn(`⚠️ 修正：字段"${field.field}"的should_be与extracted相同，移到accurate_fields`);
            auditResult.accurate_fields = auditResult.accurate_fields || [];
            auditResult.accurate_fields.push(field.field);
            movedToIssues.push(`${field.field}字段提取准确，但${field.reason}`);
            continue;
          }

          // 检查2: 如果reason中包含"在原文中存在"或"真实存在"等字样，说明不应该在inaccurate_fields中
          if (field.reason && (
            field.reason.includes('在原文中存在') || 
            field.reason.includes('真实存在') ||
            field.reason.includes('原文中确实有') ||
            field.reason.includes('是原文中') ||
            field.reason.includes('不构成错误')
          )) {
            console.warn(`⚠️ 修正：字段"${field.field}"在原文中存在，移到accurate_fields`);
            auditResult.accurate_fields = auditResult.accurate_fields || [];
            auditResult.accurate_fields.push(field.field);
            movedToIssues.push(`${field.field}提取值"${field.extracted}"在原文中存在，但建议使用"${field.should_be}"以保持一致性`);
            continue;
          }

          // 检查3: 清理reason字段中的重复内容和过长文本
          if (field.reason) {
            const originalReason = field.reason;
            let cleanedReason = field.reason;
            
            // 检测并移除重复内容（查找重复的句子模式）
            const sentences = cleanedReason.split(/[，。；]/).filter(s => s.trim());
            if (sentences.length > 3) {
              // 检测是否有大量重复句子
              const uniqueSentences = [...new Set(sentences)];
              if (sentences.length > uniqueSentences.length * 2) {
                console.warn(`⚠️ 清理：字段"${field.field}"的reason包含大量重复内容`);
                // 只保留前3个不重复的句子
                cleanedReason = uniqueSentences.slice(0, 3).join('，');
              }
            }
            
            // 限制reason长度（最多100字）
            if (cleanedReason.length > 100) {
              console.warn(`⚠️ 清理：字段"${field.field}"的reason过长（${cleanedReason.length}字），截断到100字`);
              cleanedReason = cleanedReason.substring(0, 100) + '...';
            }
            
            if (cleanedReason !== originalReason) {
              field.reason = cleanedReason;
              console.log(`✓ 已清理字段"${field.field}"的reason（原长度: ${originalReason.length}字 → 新长度: ${cleanedReason.length}字）`);
            }
          }

          // 保留真正不准确的字段
          validInaccurateFields.push(field);
        }

        // 更新inaccurate_fields
        auditResult.inaccurate_fields = validInaccurateFields;

        // 将移动的问题添加到issues中
        if (movedToIssues.length > 0) {
          auditResult.issues = auditResult.issues || [];
          auditResult.issues.push(...movedToIssues);
          console.log(`✓ 已将${movedToIssues.length}个字段从inaccurate_fields移到issues`);
        }

        // 如果修正后没有不准确字段了，提高评分
        if (auditResult.inaccurate_fields.length === 0 && auditResult.overall_score < 90) {
          const oldScore = auditResult.overall_score;
          auditResult.overall_score = Math.max(90, auditResult.overall_score);
          auditResult.recommendation = '通过';
          console.log(`✓ 评分从${oldScore}提升到${auditResult.overall_score}分`);
        }
      }

      console.log(`✓ AI审核完成，评分: ${auditResult.overall_score}分，建议: ${auditResult.recommendation}`);
      console.log(`  - 准确字段: ${auditResult.accurate_fields?.length || 0}个`);
      console.log(`  - 不准确字段: ${auditResult.inaccurate_fields?.length || 0}个`);
      console.log(`  - 需要关注问题: ${auditResult.issues?.length || 0}个`);

      return {
        success: true,
        ...auditResult
      };
    } catch (error) {
      console.error('AI审核失败:', error.message);
      throw error;
    }
  }

  /**
   * 解析时间字符串，提取年份用于排序
   * @param {string} timeStr - 时间字符串（如：2012、1997至2001、2012至今）
   * @returns {number} - 年份数字，如果无法解析返回0
   */
  parseYear(timeStr) {
    if (!timeStr) return 0;
    
    // 移除"至今"等文字
    timeStr = timeStr.replace(/至今|to present|present|now/gi, '').trim();
    
    // 提取所有4位数字（年份）
    const years = timeStr.match(/\d{4}/g);
    if (!years || years.length === 0) return 0;
    
    // 如果有多个年份（日期范围），取最后一个（结束年份）
    return parseInt(years[years.length - 1]);
  }

  /**
   * 从教育经历中提取最高学历和院校
   * @param {Array} educationExperience - 教育经历数组
   * @returns {Object} - { highestDegree: '最高学历', graduateSchool: '毕业院校' }
   */
  extractHighestEducation(educationExperience) {
    if (!educationExperience || !Array.isArray(educationExperience) || educationExperience.length === 0) {
      return { highestDegree: null, graduateSchool: null };
    }

    // 过滤掉内容为空的记录（只保留有实质内容的）
    const validEducationExperience = educationExperience.filter(exp => {
      const hasContent = (exp.school && exp.school.trim()) || 
                       (exp.major && exp.major.trim());
      return hasContent;
    });

    if (validEducationExperience.length === 0) {
      return { highestDegree: null, graduateSchool: null };
    }

    // 学历等级映射（用于排序）
    const degreeRank = {
      'phd': 6, 'ph.d': 6, 'ph.d.': 6, 'doctor': 6, 'doctorate': 6, 'doctoral': 6, '博士': 6,
      'master': 5, "master's": 5, 'masters': 5, 'mba': 5, 'msc': 5, 'ma': 5, '硕士': 5,
      'bachelor': 4, "bachelor's": 4, 'bachelors': 4, 'bs': 4, 'ba': 4, 'undergraduate': 4, '学士': 4, '本科': 4,
      'associate': 3, 'college': 3, '专科': 3, '大专': 3,
      'high school': 2, 'secondary': 2, '高中': 2, '中学': 2,
      'primary': 1, 'elementary': 1, '小学': 1
    };

    // 为每条教育经历记录添加等级和年份信息
    const educationWithRank = validEducationExperience.map(edu => {
      const eduText = `${edu.major || ''} ${edu.school || ''}`.toLowerCase();
      let rank = 0;
      
      // 查找匹配的学历等级
      for (const [degree, r] of Object.entries(degreeRank)) {
        if (eduText.includes(degree)) {
          rank = r;
          break;
        }
      }
      
      // 解析年份
      const year = this.parseYear(edu.time);
      
      return {
        ...edu,
        rank,
        year
      };
    });

    // 找出最高学历等级
    const maxRank = Math.max(...educationWithRank.map(e => e.rank));
    
    if (maxRank === 0) {
      // 如果没有匹配到学历关键词，返回时间最近的记录
      const sortedByTime = educationWithRank.sort((a, b) => b.year - a.year);
      const latest = sortedByTime[0];
      return {
        highestDegree: latest?.major || null,
        graduateSchool: latest?.school || null
      };
    }

    // 筛选出所有最高学历等级的记录
    const highestDegreeEducations = educationWithRank.filter(e => e.rank === maxRank);
    
    // 如果有多个相同等级的学历，选择时间最近的
    const sortedByTime = highestDegreeEducations.sort((a, b) => b.year - a.year);
    const highestEdu = sortedByTime[0];

    console.log(`🎓 最高学历: ${highestEdu.school} - ${highestEdu.major} (${highestEdu.year}年)`);

    return {
      highestDegree: highestEdu?.major || null,
      graduateSchool: highestEdu?.school || null
    };
  }

  /**
   * 修复教育信息：从中文简介中提取最新日期的最高学历和对应院校
   * @param {string} chineseBiography - 中文简介
   * @returns {Promise<Object>} - 修复后的教育信息
   */
  async fixEducationInfo(chineseBiography) {
    try {
      console.log('🎓 开始修复教育信息...');

      const prompt = `请仔细分析以下中文简介中的教育背景信息，找出**获得时间最晚的最高学历**及其对应的毕业院校。

# 规则说明：

1. **只选择正式学位**：
   - ✅ 必须包含"学位"字样：学士学位、硕士学位、博士学位
   - ❌ "优秀毕业生"、"毕业生"不是学位，不能选择
   - ❌ 军校课程（中队军官学校、空军司令部参谋学院等）的"毕业"或"优秀毕业生"不是学位
   
2. **最高学历**定义：
   - 博士学位 > 硕士学位 > 学士学位
   - 如果有多个相同级别的学位（如多个硕士），选择**时间最晚**的那个
   
3. **毕业院校**：
   - 必须是获得最高学历的那个院校
   - 短期培训、研讨会、函授课程不算学位，不应选择
   
4. **排除非学位教育**：
   - ❌ 培训班、培训课程
   - ❌ 研讨会、讲习班
   - ❌ 短期课程、进修课程
   - ❌ 军校短期培训（中队军官学校、空军司令部参谋学院等）
   - ❌ "优秀毕业生"、"荣誉毕业生"等荣誉称号
   - ❌ 任何不包含"学位"字样的教育经历

# 中文简介：
${chineseBiography}

# 请返回JSON格式（只返回JSON，不要任何解释）：
{
  "education_level": "最高学历的学位名称（如：战略研究硕士学位、航空航天工程硕士学位、航空与航天学学士学位等）",
  "graduate_school": "最高学历对应的毕业院校中文名（如：空军战争学院、斯坦福大学、麻省理工学院等）",
  "education_year": "获得该学历的年份（如：2015）",
  "reason": "选择理由（简短说明为什么选择这个学历和院校）"
}

# 重要示例：
如果教育背景包含：
- 1994年：麻省理工学院，航空与航天学学士学位
- 1995年：斯坦福大学，航空航天工程硕士学位
- 2001年：中队军官学校，优秀毕业生
- 2007年：空军司令部参谋学院，优秀毕业生

正确提取应该是：
- education_level: "航空航天工程硕士学位"
- graduate_school: "斯坦福大学"
- 说明：2001-2007年的"优秀毕业生"都不是正式学位，只是军校短期培训的荣誉称号

注意：
- 必须选择正式的学位教育（学士学位、硕士学位、博士学位）
- 在同级学位中选择时间最新的`;

      const response = await axios.post(
        config.llm.apiUrl,
        {
          model: config.llm.model,
          messages: [
            {
              role: 'user',
              content: prompt
            }
          ],
          temperature: 0.1  // 低温度，更确定性的输出
        },
        {
          headers: {
            'Authorization': `Bearer ${config.llm.apiToken}`,
            'Content-Type': 'application/json'
          },
          timeout: 60000
        }
      );

      const content = response.data.choices[0].message.content;
      
      // 解析JSON
      let result;
      try {
        const jsonStr = content.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim();
        result = JSON.parse(jsonStr);
      } catch (parseError) {
        console.error('教育信息JSON解析失败:', content);
        throw new Error('教育信息格式不正确');
      }

      console.log(`✓ 教育信息修复完成:`);
      console.log(`  - 最高学历: ${result.education_level}`);
      console.log(`  - 毕业院校: ${result.graduate_school}`);
      console.log(`  - 年份: ${result.education_year}`);
      console.log(`  - 理由: ${result.reason}`);

      return {
        success: true,
        education_level: result.education_level,
        graduate_school: result.graduate_school,
        education_year: result.education_year,
        reason: result.reason
      };
    } catch (error) {
      console.error('修复教育信息失败:', error.message);
      throw error;
    }
  }

  /**
   * 翻译武器装备的关键字段
   * @param {Object} basicInfo - 基本信息对象
   * @returns {Promise<void>}
   */
  async translateWeaponFields(basicInfo) {
    const fieldsToTranslate = [
      { key: 'development_background', label: '研制背景' },
      { key: 'construction_history', label: '建造沿革' },
      { key: 'service_history', label: '服役历程' },
      { key: 'structural_features', label: '结构特征' },
      { key: 'equipment', label: '配备装备' },
      { key: 'power_system', label: '动力系统' }
    ];

    for (const field of fieldsToTranslate) {
      if (basicInfo[field.key] && basicInfo[field.key].trim()) {
        try {
          console.log(`  - 翻译${field.label}...`);
          const translatedText = await this.translateToChineseSimple(basicInfo[field.key]);
          if (translatedText) {
            basicInfo[field.key] = translatedText;
            console.log(`    ✓ ${field.label}翻译完成（${translatedText.length}字符）`);
          }
        } catch (error) {
          console.warn(`    ⚠ ${field.label}翻译失败:`, error.message);
          // 翻译失败时保留原文
        }
      }
    }
  }

  /**
   * 递归翻译战技指标的中文名称
   * @param {Array} specifications - 战技指标数组
   * @returns {Promise<void>}
   */
  async translateSpecifications(specifications) {
    if (!specifications || !Array.isArray(specifications) || specifications.length === 0) {
      return;
    }

    for (const spec of specifications) {
      // 翻译 indicator_chinese 字段（如果原文是英文）
      if (spec.indicator_chinese && spec.indicator_chinese.trim()) {
        // 简单判断是否为英文（包含字母且中文字符少）
        const chineseChars = (spec.indicator_chinese.match(/[\u4e00-\u9fa5]/g) || []).length;
        const totalChars = spec.indicator_chinese.length;
        const isEnglish = chineseChars < totalChars * 0.3; // 中文字符少于30%认为是英文
        
        if (isEnglish) {
          try {
            const translatedText = await this.translateToChineseSimple(spec.indicator_chinese);
            if (translatedText) {
              spec.indicator_chinese = translatedText;
            }
          } catch (error) {
            console.warn(`    ⚠ 指标名称翻译失败: ${spec.indicator_chinese}`, error.message);
            // 翻译失败时保留原文
          }
        }
      }

      // 递归翻译子节点
      if (spec.children && Array.isArray(spec.children) && spec.children.length > 0) {
        await this.translateSpecifications(spec.children);
      }
    }
  }

  /**
   * 简单的中文翻译（用于武器装备字段）
   * @param {string} text - 英文文本
   * @returns {Promise<string>} - 中文文本
   */
  async translateToChineseSimple(text) {
    if (!text || text.trim() === '') {
      return text;
    }

    try {
      const response = await axios.post(
        config.translate.apiUrl,
        {
          model: config.translate.model,
          messages: [
            {
              role: 'user',
              content: `将以下英文翻译为简体中文，保持原文的详细程度和完整性，不要省略任何信息：

${text}

请直接返回中文翻译，不要包含任何解释。`
            }
          ],
          temperature: 0.3
        },
        {
          headers: {
            'Content-Type': 'application/json'
          },
          timeout: 60000
        }
      );

      const translatedText = response.data.choices[0].message.content.trim();
      return translatedText;
    } catch (error) {
      console.error('翻译失败:', error.message);
      throw error;
    }
  }

  /**
   * 从武器装备介绍中抽取结构化信息
   * @param {string} description - 装备介绍（Markdown格式）
   * @param {Object} context - 可选的上下文信息（如装备名称）
   * @returns {Promise<Object>} - 抽取的结构化数据
   */
  async extractWeaponInfo(description, context = {}) {
    const originalLength = description ? description.length : 0;
    console.log(`📝 装备介绍长度: ${originalLength} 字符`);

    const processedDescription = description || '';

    // 检测介绍语言
    const isChinese = /[\u4e00-\u9fa5]/.test(processedDescription);
    const languageHint = isChinese ? '中文' : '英文';
    console.log(`📝 介绍语言: ${languageHint}`);

    // 构建上下文提示
    let contextHint = '';
    if (context.name) {
      contextHint = `\n**已知装备名称：** ${context.name}\n`;
      console.log(`📌 装备名称: ${context.name}`);
    }

    const maxRetries = 2;
    let lastError;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        if (attempt > 1) {
          console.log(`第 ${attempt} 次尝试调用LLM...`);
        }

        const prompt = `从以下武器装备介绍中提取结构化信息，仅返回有效的JSON格式数据（不要任何解释）。
${contextHint}
装备介绍：
${processedDescription}

**JSON格式要求（非常重要）：**
- 必须返回严格符合JSON标准的格式
- 所有字段名必须用双引号包围
- 字符串值必须用双引号
- 数字值不要用引号
- 不要在最后一个字段后加逗号
- 确保所有括号正确配对

返回JSON格式：
{
  "basic_info": {
    "origin_country": "原产国（中文名称）",
    "category": "装备分类（如：战斗机、坦克、驱逐舰等）",
    "chinese_name": "中文名称",
    "english_name": "英文名称",
    "chinese_intro": "中文简介（200-500字的概括性介绍）",
    "english_intro": "英文简介（如果原文有英文内容）",
    "thumbnail": "缩略图URL（如果有）",
    "contractor": "承包商/制造商",
    "service_time": "服役时间（如：1990年、1990-2010等）",
    "development_background": "研制背景（详细且完整地说明研制原因、时代背景、技术需求等，保留原文所有相关内容）",
    "construction_history": "建造沿革（详细且完整地说明设计过程、建造历史、改进升级等，保留原文所有相关内容）",
    "service_history": "服役历程（详细且完整地说明服役经历、参与的战役、部署情况等，保留原文所有相关内容）",
    "structural_features": "结构特征（详细且完整地说明外观、结构设计、技术特点等，保留原文所有相关内容）",
    "equipment": "配备装备（详细且完整地列举配备的武器、电子设备、传感器等，保留原文所有相关内容）",
    "power_system": "动力系统（详细且完整地说明发动机、推进系统、性能参数等，保留原文所有相关内容）",
    "status": "当前状态（如：在役、退役、研发中等）",
    "data_source": "数据来源"
  },
  "specifications": [
    {
      "node_type": "category",
      "indicator_chinese": "分类中文名称（如：尺寸、性能等）",
      "indicator_english": "分类英文名称",
      "children": [
        {
          "node_type": "indicator",
          "indicator_chinese": "指标中文名称",
          "indicator_english": "指标英文名称",
          "indicator_value": "指标值（带单位）"
        }
      ]
    }
  ],
  "users": [
    {
      "country": "使用国家",
      "variant": "装备变体/型号",
      "operator_chinese": "运营商中文名称（如：美国空军、中国海军等）",
      "operator_english": "运营商英文名称",
      "operator_intro": "运营商简介",
      "order_quantity": "订单数量（如：100架、50辆等，包含单位）"
    }
  ]
}

**重要说明：**
- **basic_info（基本信息）**：务必尽可能详细、完整地提取所有信息，特别是以下字段必须包含原文的所有相关段落和细节：
  * development_background（研制背景）：提取所有关于为什么研制、时代背景、需求分析的内容
  * construction_history（建造沿革）：提取所有关于设计、建造、改进、升级的历史内容
  * service_history（服役历程）：提取所有关于服役、部署、战役、使用历史的内容
  * structural_features（结构特征）：提取所有关于外观、结构、设计特点的内容
  * equipment（配备装备）：提取所有关于武器、设备、传感器的详细信息
  * power_system（动力系统）：提取所有关于发动机、推进、动力性能的内容
- **不要省略或概括这些字段的内容，要保持原文的详细程度和完整性**
- **specifications（战技指标）**：使用树形结构组织，支持多级分类：
  * node_type="category" 表示分类节点（如"尺寸"、"性能"），分类名称保存在 indicator_chinese 和 indicator_english 字段
  * node_type="indicator" 表示具体指标（有数值），包含 indicator_chinese、indicator_english、indicator_value
  * 所有节点统一使用 indicator_chinese 和 indicator_english 字段存储名称
  * 可以嵌套多层，通过 children 数组表示子节点
  * 例如：Dimensions（分类） -> Overall（分类） -> length（分类） -> rotors turning: 16.18 m（指标）
- **users（使用用户）**：列出所有使用该装备的国家和运营商，包含订单数量信息
- **如果某个字段在原文中没有明确提到，使用null或空字符串""，不要推测**
- **中文简介应该是对整个装备的概括性介绍，200-500字**

请严格按照JSON格式返回，不要包含任何其他文字。`;

        const response = await axios.post(
          config.llm.apiUrl,
          {
            model: config.llm.model,
            messages: [
              {
                role: 'user',
                content: prompt
              }
            ],
            temperature: 0.1,
            max_tokens: 16000  // 增加最大输出token数，支持更详细的提取
          },
          {
            headers: {
              'Authorization': `Bearer ${config.llm.apiToken}`,
              'Content-Type': 'application/json'
            },
            timeout: 180000 // 180秒超时，因为装备信息可能比较长且需要详细提取
          }
        );

        const content = response.data.choices[0].message.content;
        console.log(`📥 LLM返回内容长度: ${content.length} 字符`);

        // 解析JSON
        let jsonStr = content;
        
        // 移除Markdown代码块标记
        jsonStr = jsonStr.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim();
        
        // 修复常见JSON错误
        jsonStr = this.fixCommonJsonErrors(jsonStr);

        console.log('🔍 开始解析JSON...');
        const result = JSON.parse(jsonStr);

        // 验证返回数据结构
        if (!result.basic_info) {
          throw new Error('缺少basic_info字段');
        }

        console.log('✅ 装备信息抽取成功');
        console.log(`  - 装备名称: ${result.basic_info.chinese_name || result.basic_info.english_name}`);
        console.log(`  - 原产国: ${result.basic_info.origin_country}`);
        console.log(`  - 分类: ${result.basic_info.category}`);
        console.log(`  - 战技指标数: ${result.specifications ? result.specifications.length : 0}`);
        console.log(`  - 使用用户数: ${result.users ? result.users.length : 0}`);

        // 如果是英文内容，翻译关键字段为中文
        if (!isChinese) {
          console.log('📝 检测到英文内容，开始翻译关键字段...');
          
          // 翻译基本信息字段
          if (result.basic_info) {
            await this.translateWeaponFields(result.basic_info);
          }
          
          // 翻译战技指标名称
          if (result.specifications && result.specifications.length > 0) {
            console.log('  - 翻译战技指标名称...');
            await this.translateSpecifications(result.specifications);
            console.log(`    ✓ 战技指标翻译完成`);
          }
        }

        return {
          success: true,
          data: result
        };

      } catch (error) {
        lastError = error;
        console.error(`❌ 第 ${attempt} 次尝试失败:`, error.message);
        
        if (attempt < maxRetries) {
          console.log('等待3秒后重试...');
          await new Promise(resolve => setTimeout(resolve, 3000));
        }
      }
    }

    // 所有重试都失败
    console.error('❌ LLM调用失败（已重试多次）');
    throw new Error(`装备信息抽取失败: ${lastError.message}`);
  }
}

module.exports = new LLMService();

